//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Reverse points in polyline/polygon.
//

# include "StdAfx.h"
# include "ReversePoints.h"
# include "UndoRedo.h"
# include "Label.h"
# include "CombinedAction.h"

class CReversePointsAction : public CEditAction {
  public:
	CReversePointsAction (rgn_t * _pRgn, size_t _cElement) :
		m_pRgn (_pRgn),
		m_cElement (_cElement)
	{}

	virtual void GetName (string_t & _str) const;
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	rgn_t * const m_pRgn;
	size_t  const m_cElement;
};

void CReversePointsAction::GetName (string_t & _str) const {
	CCritSec::CLock lock (g_cs);

	_str.append ("reverse points [");
	GetHintText (* m_pRgn, _str);
	_str.append ("]");
}

bool CReversePointsAction::Do () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgn);
	assert (m_pRgn->Kind == rkPolyline || m_pRgn->Kind == rkPolygon);

	assert (m_cElement < m_pRgn->elements.size ());
	rgn_element_t & element = m_pRgn->elements [m_cElement];

	// Fix turn restrictions.
	if (m_pRgn->HasRGNodes ()) {
		for (rgrestrictions_t::iterator r = g_map.rgrestrictions.begin (); r != g_map.rgrestrictions.end (); ++ r) {
			rgrestriction_t & restriction = r->second;
			if (! restriction.IsUsed ())
				continue;

			if (restriction.pFromRoad == m_pRgn) {
				const rgnode_t * const pPrevNode = element.GetPrevNode (restriction.pNode, restriction.bFromDirection != 0);
				if (
					pPrevNode != NULL &&
					element.HasNode (pPrevNode) &&
					element.HasNode (restriction.pNode)
				)
					restriction.bFromDirection = ! restriction.bFromDirection;
			}
			if (restriction.pToRoad == m_pRgn) {
				const rgnode_t * const pNextNode = element.GetNextNode (restriction.pNode, restriction.bToDirection != 0);
				if (
					pNextNode != NULL &&
					element.HasNode (pNextNode) &&
					element.HasNode (restriction.pNode)
				)
					restriction.bToDirection = ! restriction.bToDirection;
			}
		}
	}

	// Reverse points.
	points_t & points = element.points;
	std::reverse (points.begin (), points.end ());

	// Reverse point extras.
	if (element.pExtra) {
		point_extras_t & PointExtras = element.pExtra->PointExtras;
		if (! PointExtras.empty ()) {
			const size_t cPoints = points.size ();
			PointExtras.resize (cPoints);
			std::reverse (PointExtras.begin (), PointExtras.end ());

			if (HasNumbers (PointExtras)) {
				// Reverse house numbers.
				numbers_t * pNumbersPrev = NULL;
				for (size_t c = 0; c < cPoints; ++ c) {
					numbers_t & Numbers = PointExtras [c].Numbers;
					if (! Numbers.bHasNumbers)
						continue;

					Numbers.ReverseSides (pNumbersPrev, c == cPoints - 1);

					pNumbersPrev = & Numbers;
				}
			}
		}
	}

//	m_pRgn->RefreshAttributes (g_map, false);

	return true;
}

void CReversePointsAction::Undo () {
	CReversePointsAction::Do ();
}

void CReversePointsAction::Redo () {
	CReversePointsAction::Do ();
}

//////////////////////////////////////////

void ReversePoints (rgn_t * _pRgn, size_t _cElement) {
	assert (_pRgn);
	assert (_pRgn->Kind == rkPolyline || _pRgn->Kind == rkPolygon);
	assert (_cElement < _pRgn->elements.size ());

	PerformEditAction (new CReversePointsAction (_pRgn, _cElement));
}

bool IsReversePolylineAppliable () {
	bool bAppliable = false;

	const size_t cSelItems = g_Selection.size ();
	for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
		find_info_t & SelItem = g_Selection [cSelItem];
		if (! SelItem.pLayer)
			continue;

		rgn_t & rgn = * SelItem.iRgn;			
		if (rgn.IsDeleted () || ! rgn.IsPolyline ())
			continue;

		const size_t cElements = rgn.elements.size ();
		for (size_t cElement = 0; cElement < cElements; ++ cElement) {
			rgn_element_t & element = rgn.elements [cElement];
			if (element.bDeleted)
				continue;

			if (
				SelItem.cElementOrOrigin == -1 ||
				SelItem.cElementOrOrigin == cElement
			) {
				bAppliable = true;
			}
		}
	}

	return bAppliable;
}

void ReverseSelectedPolylines () {
	const size_t cDepth = BeginCombinedAction ();

	const size_t cSelItems = g_Selection.size ();
	for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
		find_info_t & SelItem = g_Selection [cSelItem];
		if (! SelItem.pLayer)
			continue;

		rgn_t & rgn = * SelItem.iRgn;			
		if (rgn.IsDeleted () || ! rgn.IsPolyline ())
			continue;

		const size_t cElements = rgn.elements.size ();
		for (size_t cElement = 0; cElement < cElements; ++ cElement) {
			rgn_element_t & element = rgn.elements [cElement];
			if (element.bDeleted)
				continue;

			if (
				SelItem.cElementOrOrigin == -1 ||
				SelItem.cElementOrOrigin == cElement
			)
				ReversePoints (& rgn, cElement);
		}
	}

	EndCombinedAction (cDepth, "reverse polyline(s)");
}